﻿using System;
using System.Collections.Generic;
using System.Linq;
using MyIocContainer.Exceptions;

namespace MyIocContainer
{
    public class MyContainer : IContainer
    {
        private readonly IList<RegisteredClass> _registeredObjects = new List<RegisteredClass>();

        public void Register<TAbstract, TConcrete>()
        {
            Register<TAbstract,TConcrete>(LifeCycle.Singleton);
        }

        public void Register<TAbstract, TConcrete>(LifeCycle lifeCycle)
        {
            _registeredObjects.Add(new RegisteredClass(typeof(TAbstract),typeof(TConcrete),lifeCycle));
        }

        public TTypeResolve Resolve<TTypeResolve>()
        {
            return (TTypeResolve)ResolveObject(typeof (TTypeResolve));
        }

        public object Resolve(Type typeToResolve)
        {
            return ResolveObject(typeToResolve);
        }


        private object ResolveObject(Type typeToResolve)
        {
            var registeredObject = _registeredObjects.FirstOrDefault(o => o.TypeToResolve == typeToResolve);
            if(registeredObject == null)
            {
                throw new TypeNotRegisteredException(string.Format("Typ {0} nie został zarejestrowany w kontenerze",typeToResolve.Name));
            }
            return GetInstance(registeredObject);
        }

        private object GetInstance(RegisteredClass registeredClass)
        {
            if(registeredClass.Instance == null || registeredClass.LifeCycle == LifeCycle.Transient)
            {
                var parameters = ResolveConstructorParameters(registeredClass);
                registeredClass.CreateInstance(parameters.ToArray());
            }

            return registeredClass.Instance;
        }

        private IEnumerable<object> ResolveConstructorParameters(RegisteredClass registeredClass)
        {
            var constructorInfo = registeredClass.ConcreteType.GetConstructors().First();
            return constructorInfo.GetParameters().Select(info => ResolveObject(info.ParameterType));
        }
    }
}
